/*
 * 
 */

package edu.uhcl.csci4838.group3.pendingIntents.notifications;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.text.format.DateFormat;
import android.util.Log;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorCourse;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorCourseAlert;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorCourseTimeOverride;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorHoliday;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorSemester;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorTask;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorTaskAlert;
import edu.uhcl.csci4838.group3.database.info.Course;
import edu.uhcl.csci4838.group3.database.info.CourseTimeOverride;
import edu.uhcl.csci4838.group3.database.info.Holiday;
import edu.uhcl.csci4838.group3.database.info.Semester;
import edu.uhcl.csci4838.group3.date.SimpleTime;
import edu.uhcl.csci4838.group3.pendingIntents.AllIntents;
import edu.uhcl.csci4838.group3.pendingIntents.IntentExtraStringStorage;
import edu.uhcl.csci4838.group3.pendingIntents.OnAlarmReceiver;

/**
 * manages all notification
 * 
 * @author Tyler Hays
 */
public class ReminderMngr implements IntentExtraStringStorage {
	
	private static final String TAG = "ReminderMng";
	
	/**
	 * XXX
	 * 
	 * @param startInt
	 *            start time of course Hour
	 * @param startMin
	 *            start time of course minute
	 * @param endHour
	 *            end time of course hour
	 * @param endMin
	 *            end time of course min
	 * @return string of time
	 */
	private static String courseStringBuilder(final int startInt,
			final int startMin,
			final int endHour,
			final int endMin,
			final GregorianCalendar gcal) {
		final StringBuilder sb = new StringBuilder();
		SimpleTime simpleTimeStart;
		SimpleTime simpleTimeEnd;
		
		simpleTimeStart = new SimpleTime(startInt,
				startMin);
		simpleTimeEnd = new SimpleTime(endHour,
				endMin);
		
		sb.append(DateFormat.format("EEEE",
				gcal));
		sb.append("  ");
		sb.append(simpleTimeStart.toString());
		sb.append(" - ");
		sb.append(simpleTimeEnd.toString());
		
		sb.trimToSize();
		return sb.toString();
	}
	
	/**
	 * Context all wide
	 */
	private final Context mContext;
	
	/**
	 * AlarmManager
	 */
	private final AlarmManager mAlarmManager;
	
	/**
	 * Constuctor that context needs to be made and makes Alarm_Service
	 * 
	 * @param context
	 *            context
	 */
	public ReminderMngr(final Context context) {
		this.mContext = context;
		this.mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
	}
	
	/**
	 * cancel course alert
	 * 
	 * @param courseCursor
	 *            course needing to have alerted canceled
	 * @param courseAlert
	 *            alert needing to be canceled
	 */
	public void cancelReminder(final CursorCourse courseCursor,
			final CursorCourseAlert courseAlert) {
		
		final Long courseStartDate = courseCursor.getColStartDate();
		final Long courseEndDate = courseCursor.getColEndDate();
		final Integer startTimeHour = courseCursor.getColStartTimeHour();
		final Integer startTimeMinute = courseCursor.getColStartTimeMinute();
		final Integer endTimeHour = courseCursor.getColEndTimeHour();
		final Integer endTimeMinute = courseCursor.getColEndTimeMinute();
		
		// checkings to make sure nothing is missing if so just leaves
		if ((courseStartDate == null)
				|| (courseEndDate == null)
				|| (startTimeHour == null)
				|| (startTimeMinute == null)
				|| (endTimeHour == null)
				|| (endTimeMinute == null)) {
			Log.e(ReminderMngr.TAG,
					"Missing data");
			return;
		}
		
		// checking to make sure course has not ended yet
		if (courseEndDate.longValue() < Calendar.getInstance()
				.getTimeInMillis()) {
			return;
		}
		this.setCourseReminder(courseStartDate,
				courseEndDate,
				startTimeHour,
				startTimeMinute,
				endTimeHour,
				endTimeMinute,
				courseCursor,
				courseAlert);
		
		/*
		 * local variables that do not need to be called if any values are null
		 */
		
	}
	
	/**
	 * Sets up notification.
	 * 
	 * @param taskCursor
	 *            Cursor for Task
	 * @param taskAlertCursor
	 *            cursor for Alert
	 */
	public void cancelReminder(final CursorTask taskCursor,
			final CursorTaskAlert taskAlertCursor) {
		
		int taskAlertID;
		
		taskAlertID = (int) taskAlertCursor.getColId();
		this.cancelReminder(taskCursor,
				taskAlertID);
	}
	
	/**
	 * Sets up notifications with requested info.
	 * 
	 * @param taskCursor
	 *            Cursor for task
	 * @param taskAlertID
	 * @param when
	 *            time for the notification
	 */
	public void cancelReminder(final CursorTask taskCursor,
			final int taskAlertID) {
		
		final NotificationAlertLocation alertLocation = new NotificationAlertLocation(edu.uhcl.csci4838.group3.database.info.Task.TABLE_NAME,
				taskCursor.getColId());
		
		this.cancelReminder(alertLocation,
				taskAlertID,
				taskCursor.getColCourseName(),
				taskCursor.getColName(),
				taskCursor.getColDateDue());
	}
	
	/**
	 * XXX
	 * 
	 * @param courseCursor
	 * @param courseAlert
	 * @param context
	 * @param semsterCuror
	 */
	public void setReminder(final CursorCourse courseCursor,
			final CursorCourseAlert courseAlert) {
		
		courseAlert.getColId();
		final Long courseStartDate = courseCursor.getColStartDate();
		final Long courseEndDate = courseCursor.getColEndDate();
		final int dayBitField = courseCursor.getColDayBitField();
		courseAlert.getColMinutesBefore();
		
		final Integer startTimeHour = courseCursor.getColStartTimeHour();
		final Integer startTimeMinute = courseCursor.getColStartTimeMinute();
		final Integer endTimeHour = courseCursor.getColEndTimeHour();
		final Integer endTimeMinute = courseCursor.getColEndTimeMinute();
		
		final GregorianCalendar gcal = new GregorianCalendar();
		AllIntents.setMeetingDays(dayBitField);
		Calendar.getInstance();
		Calendar.getInstance();
		
		// checkings to make sure nothing is missing if so just leaves
		if ((courseStartDate == null)
				|| (courseEndDate == null)
				|| (startTimeHour == null)
				|| (startTimeMinute == null)
				|| (endTimeHour == null)
				|| (endTimeMinute == null)) {
			Log.e(ReminderMngr.TAG,
					"Missing data");
			Log.e(ReminderMngr.TAG,
					courseCursor.getColName());
			return;
		}
		
		// checking to make sure course has not ended yet
		if (courseEndDate.longValue() < Calendar.getInstance()
				.getTimeInMillis()) {
			return;
		}
		
		/*
		 * local variables that do not need to be called if any values are null
		 */
		final ContentValues contentValues = new ContentValues();
		final BackpackEduDatabaseHelper dbhelper = new BackpackEduDatabaseHelper(this.mContext);
		CursorSemester semesterCursor;
		CursorHoliday holidayCursor;
		CursorCourseTimeOverride overrideCursor;
		
		contentValues.put(Semester.ID,
				Long.valueOf(courseCursor.getColSemesterId()));
		semesterCursor = dbhelper.getSemester(contentValues,
				null,
				true);
		semesterCursor.moveToFirst();
		
		holidayCursor = dbhelper.getHoliday(contentValues,
				null,
				true);
		holidayCursor.moveToFirst();
		
		contentValues.put(Course.ID,
				Long.valueOf(courseCursor.getColId()));
		overrideCursor = dbhelper.getCourseTimeOverride(contentValues,
				null,
				true);
		
		gcal.set(gcal.get(Calendar.YEAR),
				gcal.get(Calendar.MONTH),
				gcal.get(Calendar.DAY_OF_MONTH));
		
		// checks to see if the current time is before the start date of the
		// class
		if (gcal.getTimeInMillis() < courseStartDate.longValue()) {
			gcal.setTimeInMillis(courseStartDate.longValue());
		}
		this.setCourseReminder(courseStartDate,
				courseEndDate,
				startTimeHour,
				startTimeMinute,
				endTimeHour,
				endTimeMinute,
				courseCursor,
				courseAlert);
		
		semesterCursor.close();
		holidayCursor.close();
		overrideCursor.close();
		dbhelper.close();
		return;
		
	}
	
	/**
	 * Sets up notification.
	 * 
	 * @param taskCursor
	 *            Cursor for Task
	 * @param taskAlertCursor
	 *            cursor for Alert
	 */
	public void setReminder(final CursorTask taskCursor,
			final CursorTaskAlert taskAlertCursor) {
		
		final Calendar when = Calendar.getInstance();
		final Date taskDueDate = new Date(taskCursor.getColDateDue()
				.longValue());
		int alertMinBefore;
		int taskAlertID;
		
		taskAlertID = (int) taskAlertCursor.getColId();
		when.setTime(taskDueDate);
		alertMinBefore = taskAlertCursor.getColMinutesBefore()
				* -1;
		when.add(Calendar.MINUTE,
				alertMinBefore);
		
		if (Calendar.getInstance()
				.compareTo(when) < 0) {
			this.setTaskReminder(taskCursor,
					taskAlertID,
					when);
			Log.d(ReminderMngr.TAG,
					"Task Notification Added");
		} else {
			Log.e(ReminderMngr.TAG,
					"Skipped alert");
		}
		
	}
	
	/**
	 * Cancels alert
	 * 
	 * @param notifAlertLocation
	 *            the location in the table of the alert
	 * @param alertID
	 * @param titleString
	 *            title part of notification
	 * @param alertString
	 *            the note part of notification
	 * @param dueDate
	 */
	private void cancelReminder(final NotificationAlertLocation notifAlertLocation,
			final int alertID,
			final String titleString,
			final String alertString,
			final Long dueDate) {
		
		final Intent i = new Intent(this.mContext,
				OnAlarmReceiver.class);
		
		i.putExtra(IntentExtraStringStorage.TABLE_NAME,
				notifAlertLocation.tableName);
		i.putExtra(IntentExtraStringStorage.ROW_ID,
				notifAlertLocation.rowIDNumber);
		i.putExtra(IntentExtraStringStorage.NOTIFICATION_TITLE,
				titleString);
		i.putExtra(IntentExtraStringStorage.NOTIFICATION_NOTE,
				alertString);
		i.putExtra(IntentExtraStringStorage.DUE_TIME,
				dueDate);
		
		final PendingIntent pi = PendingIntent.getBroadcast(this.mContext,
				alertID,
				i,
				PendingIntent.FLAG_ONE_SHOT);
		pi.cancel();
		
	}
	
	/**
	 * XXX
	 * 
	 * @param courseCursor
	 * @param courseAlertID
	 * @param courseTime
	 * @param when
	 */
	private void setCourseReminder(final CursorCourse courseCursor,
			final long courseAlertID,
			final String courseTime,
			final Calendar when) {
		
		final NotificationAlertLocation notifAlertLocation = new NotificationAlertLocation(Course.TABLE_NAME,
				courseCursor.getColId());
		// courseAlertID is put in the TimeDue spot because
		// the code needs to store the information for the
		// course alerts reuse. It has nothing to do with what it really is
		this.setReminder(notifAlertLocation,
				when,
				(int) courseAlertID,
				courseCursor.getColName(),
				courseTime,
				courseAlertID);
		
	}
	
	/**
	 * Sets up notifications with requested info.
	 * 
	 * @param taskCursor
	 *            Cursor for task
	 * @param taskAlertID
	 * @param when
	 *            time for the notification
	 */
	private void setTaskReminder(final CursorTask taskCursor,
			final int taskAlertID,
			final Calendar when) {
		
		final NotificationAlertLocation alertLocation = new NotificationAlertLocation(edu.uhcl.csci4838.group3.database.info.Task.TABLE_NAME,
				taskCursor.getColId());
		
		// taskAlertID multiple by -1 to have changing IDs.
		this.setReminder(alertLocation,
				when,
				taskAlertID
						* -1,
				taskCursor.getColCourseName(),
				taskCursor.getColName(),
				taskCursor.getColDateDue());
	}
	
	/**
	 * continues with the set reminder
	 * 
	 * @param courseStartDate
	 *            course start date
	 * @param courseEndDate
	 *            course end date
	 * @param startTimeHour
	 *            course start time hour
	 * @param startTimeMinute
	 *            course star time minute
	 * @param endTimeHour
	 *            course end time hour
	 * @param endTimeMinute
	 *            course end time minute
	 * @param courseCursor
	 *            course
	 * @param courseAlert
	 *            course alert
	 */
	private void setCourseReminder(final Long courseStartDate,
			final Long courseEndDate,
			final Integer startTimeHour,
			final Integer startTimeMinute,
			final Integer endTimeHour,
			final Integer endTimeMinute,
			final CursorCourse courseCursor,
			final CursorCourseAlert courseAlert) {
		
		final int dayBitField = courseCursor.getColDayBitField();
		final int alertMinBefore = courseAlert.getColMinutesBefore();
		final long courseAlertID = courseAlert.getColId();
			
		final ContentValues contentValues = new ContentValues();
		final BackpackEduDatabaseHelper dbhelper = new BackpackEduDatabaseHelper(this.mContext);
		final List<Integer> daysOfClass = AllIntents.setMeetingDays(dayBitField);
		
		//cursors needed
		CursorSemester semesterCursor;
		CursorHoliday holidayCursor;
		CursorCourseTimeOverride overrideCursor;
	
		final GregorianCalendar gcal = new GregorianCalendar();
		final Calendar when = Calendar.getInstance();
		final Calendar alertTime = Calendar.getInstance();
		final Calendar overrideTime = Calendar.getInstance();
		final Calendar holidayEndTime = Calendar.getInstance();
		final Calendar holidayStartTime = Calendar.getInstance();
		
		boolean isOverRideTime = false;
		boolean isHoliday = false;
		
		contentValues.put(Semester.ID,
				Long.valueOf(courseCursor.getColSemesterId()));
		semesterCursor = dbhelper.getSemester(contentValues,
				null,
				true);
		semesterCursor.moveToFirst();
		
		holidayCursor = dbhelper.getHoliday(contentValues,
				new String[] { Holiday.END_DATE, Holiday.START_DATE },
				true);
		holidayCursor.moveToFirst();
		
		contentValues.put(Course.ID,
				Long.valueOf(courseCursor.getColId()));
		overrideCursor = dbhelper.getCourseTimeOverride(contentValues,
				new String[] {
						CourseTimeOverride.DATE,
						CourseTimeOverride.START_TIME_HOUR,
						CourseTimeOverride.START_TIME_MINUTE },
				true);
		gcal.set(gcal.get(Calendar.YEAR),
				gcal.get(Calendar.MONTH),
				gcal.get(Calendar.DAY_OF_MONTH));
		
		while (overrideCursor.isAfterLast() == false) {
			overrideTime.setTimeInMillis(overrideCursor.getColDate());
			overrideTime.set(Calendar.HOUR_OF_DAY,
					overrideCursor.getColStartTimeHour());
			overrideTime.set(Calendar.MINUTE,
					overrideCursor.getColStartTimeMinute());
			overrideTime.add(Calendar.MINUTE,
					alertMinBefore
							* -1);
			
			if (overrideTime.getTimeInMillis() > System.currentTimeMillis()) {
				isOverRideTime = true;
				break;
			}
			overrideCursor.moveToNext();
		}
		
		while (holidayCursor.isAfterLast() == false) {
			holidayEndTime.setTimeInMillis(holidayCursor.getColEndDate());
			
			if (holidayEndTime.getTimeInMillis() > System.currentTimeMillis()) {
				holidayStartTime.setTimeInMillis(holidayCursor.getColStartDate());
				isHoliday = true;
				break;
			}
			holidayCursor.moveToNext();
		}
		
		// checks to see if the current time is before the start date of the
		// class
		if (gcal.getTimeInMillis() < courseStartDate.longValue()) {
			gcal.setTimeInMillis(courseStartDate.longValue());
		}
		
		this.setCourseReminderDay(courseEndDate,
				startTimeHour,
				startTimeMinute,
				endTimeHour,
				endTimeMinute,
				courseCursor,
				alertMinBefore,
				courseAlertID,
				when,
				alertTime,
				gcal,
				daysOfClass,
				overrideCursor,
				overrideTime,
				holidayEndTime,
				holidayStartTime,
				isOverRideTime,
				isHoliday);
		
		semesterCursor.close();
		holidayCursor.close();
		overrideCursor.close();
		dbhelper.close();
	}
	
	/**
	 * Allows custom title and alert String
	 * 
	 * @param notifAlertLocation
	 *            the location in the table of the alert
	 * @param when
	 *            when the alarm needs to go off
	 * @param alertID
	 *            the alert id
	 * @param titleString
	 *            title part of notification
	 * @param alertString
	 *            the note part of notification
	 * @param dueTime
	 *            time task is due Can be null
	 */
	private void setReminder(final NotificationAlertLocation notifAlertLocation,
			final Calendar when,
			final int alertID,
			final String titleString,
			final String alertString,
			final Long dueTime) {
		
		final PendingIntent pi;
		
		final Intent i = new Intent(this.mContext,
				OnAlarmReceiver.class);
		i.putExtra(IntentExtraStringStorage.TABLE_NAME,
				notifAlertLocation.tableName);
		i.putExtra(IntentExtraStringStorage.ROW_ID,
				notifAlertLocation.rowIDNumber);
		i.putExtra(IntentExtraStringStorage.NOTIFICATION_TITLE,
				titleString);
		i.putExtra(IntentExtraStringStorage.NOTIFICATION_NOTE,
				alertString);
		i.putExtra(IntentExtraStringStorage.DUE_TIME,
				dueTime);
		i.putExtra(IntentExtraStringStorage.ALERT_ID,
		
				alertID);
		pi = PendingIntent.getBroadcast(this.mContext,
				alertID,
				i,
				PendingIntent.FLAG_ONE_SHOT);
		
		
		this.mAlarmManager.set(AlarmManager.RTC_WAKEUP,
				when.getTimeInMillis(),
				pi);
		
		Log.d("setReminder", when.getTime().toLocaleString()); //$NON-NLS-1$
		
		Log.d("BACKPACKEDU", "Alert Added"); //$NON-NLS-1$ //$NON-NLS-2$
	}
	
	/**
	 * loops to set the reminder day
	 * 
	 * @param courseEndDate
	 *            last day of course
	 * @param startTimeHour
	 *            start hour time
	 * @param startTimeMinute
	 *            start minute time
	 * @param endTimeHour
	 *            end hour time
	 * @param endTimeMinute
	 *            end minute time
	 * @param courseCursor
	 *            cursor for course
	 * @param alertMinBefore
	 *            minutes before class for the alert
	 * @param courseAlertID
	 *            the ID for the alert
	 * @param when
	 *            when the alert will happen
	 * @param alertTime
	 *            time for alert
	 * @param gcal
	 *            calendar
	 * @param daysOfClass
	 *            days of class
	 * @param overrideCursor
	 *            override cursor
	 * @param overrideTime
	 *            o
	 * @param holidayEndTime
	 *            end date of holiday
	 * @param holidayStartTime
	 *            start day of holiday
	 * @param isOverRideTime
	 *            is there an override
	 * @param isHoliday
	 */
	private void setCourseReminderDay(final Long courseEndDate,
			final Integer startTimeHour,
			final Integer startTimeMinute,
			final Integer endTimeHour,
			final Integer endTimeMinute,
			final CursorCourse courseCursor,
			final int alertMinBefore,
			final long courseAlertID,
			final Calendar when,
			final Calendar alertTime,
			final GregorianCalendar gcal,
			final List<Integer> daysOfClass,
			final CursorCourseTimeOverride overrideCursor,
			final Calendar overrideTime,
			final Calendar holidayEndTime,
			final Calendar holidayStartTime,
			final boolean isOverRideTime,
			final boolean isHoliday) {
		
		while (gcal.getTimeInMillis() < courseEndDate.longValue()) {
			
			// if the class is on a given day it sets everything up and then
			// checks
			// to see if the alert is before it is currently set up.
			if (daysOfClass.contains(Integer.valueOf(gcal.get(Calendar.DAY_OF_WEEK)))) {
				
				gcal.set(gcal.get(Calendar.YEAR),
						gcal.get(Calendar.MONTH),
						gcal.get(Calendar.DAY_OF_MONTH));
				// Since time is currently set to midnight moves it ahead to the
				// class start time
				gcal.set(Calendar.HOUR,
						startTimeHour.intValue());
				gcal.set(Calendar.MINUTE,
						startTimeMinute.intValue());
				
				// sets when class in calander format
				when.setTimeInMillis(gcal.getTimeInMillis());
				alertTime.setTimeInMillis(when.getTimeInMillis());
				alertTime.add(Calendar.MINUTE,
						alertMinBefore
								* -1);
				
				if ((overrideTime.getTimeInMillis() < gcal.getTimeInMillis())
						&& isOverRideTime) {
					this.setCourseReminder(courseCursor,
							courseAlertID,
							ReminderMngr.courseStringBuilder(startTimeHour.intValue(),
									startTimeMinute.intValue(),
									overrideCursor.getColEndTimeHour(),
									overrideCursor.getColEndTimeMinute(),
									gcal),
							alertTime);
					break;
				}
				
				// If alertTime is before the current alert moves on to the next
				// one.
				if ((holidayStartTime.getTimeInMillis() < gcal.getTimeInMillis())
						|| (holidayEndTime.getTimeInMillis() < gcal.getTimeInMillis())
						|| !isHoliday) {
					if (alertTime.getTimeInMillis() > Calendar.getInstance()
							.getTimeInMillis()) {
						
						Log.d(ReminderMngr.TAG,
								courseCursor.getColName()
										+ " "
										+ gcal.toString());
						;
						this.setCourseReminder(courseCursor,
								courseAlertID,
								ReminderMngr.courseStringBuilder(startTimeHour.intValue(),
										startTimeMinute.intValue(),
										endTimeHour.intValue(),
										endTimeMinute.intValue(),
										gcal),
								alertTime);
						// breaks out of the loop so does not over write next
						// intent
						break;
					}// end if (alertTime.getTimeInMillis()
				} else {
					// if
				}
				// (holidayStartTime.getTimeInMillis()<gcal.getTimeInMillis()
				
			}
			
			gcal.add(Calendar.DAY_OF_MONTH,
					1);
		}
	}

}
